// 5、使用代理对象后，每次访问属性都会收集到环境依赖，
// 展开来说当对象被proxy拦截之后，我们每次访问对象属性都会进行依赖的收集
// 但这样其实会造成很多性能的浪费，我们往往只希望在函数、方法的运行中才进行依赖的收集
// 我们有什么办法能人为的控制收集到依赖的时机吗？
// 因此我们希望能够可以改造effect函数，使之更加的智能。

import { log } from "./shared";

// ----->来自模块2的代码
const targetMap: TargetMap = new WeakMap(); // 全局映射：响应式对象--->属性依赖集合
function trigger(targetMap: TargetMap, target: any, key: string | number) {
  const depMap: DepMap | undefined = targetMap.get(target);
  if (!depMap) return;
  const dep: Dep | undefined = depMap.get(key);
  if (dep) {
    dep.forEach((effect) => {
      effect();
    });
  }
}




// ----->来自模块4的代码
const handler: BasicHandler = {
  get(target: any, key: string | number, receiver: any) {
    let res = Reflect.get(target, key, receiver);
    track(targetMap, target, key); // 注意：在这里收集追踪依赖！！
    return res;
  },
  set(target: any, key: string | number, value: any, receiver: any) {
    let previousValue: any = target[key]; // 旧值
    let res: boolean = Reflect.set(target, key, value, receiver); // 注意先后顺序，先反射再检测值是否需要触发更新
    if (previousValue !== value) {
      trigger(targetMap, target, key);
    }
    return res;
  },
};
function reactive(target: any) {
  return new Proxy(target, handler);
}




// ----->下面开始的是模块5的代码
let activeEffect: RunTimeFunction = null; // 用来记录runtime的函数，Vue 2中也运用过这个标记技巧

// 重新定义effect函数，传入参数为函数
function effect(eff: Function) {
  activeEffect = eff; // closure的处理
  activeEffect();
  activeEffect = null;
}

// 重新定义、改动依赖追踪函数
function track(targetMap: TargetMap, target: any, key: string | number) {
  // 需要先检测是否有函数正在运行中，正在运行中才进行依赖的收集
  if (activeEffect) {
    let depMap: DepMap | undefined = targetMap.get(target);
    if (!depMap) {
      depMap = new Map();
      targetMap.set(target, depMap);
    }
    let dep: Dep | undefined = depMap.get(key);
    if (!dep) {
      dep = new Set();
      depMap.set(key, dep);
    }
    dep.add(activeEffect); // 这里有改动！将正在运行的函数作为属性依赖加入dep队列中
  }
}





// 测试用例5
let total: number = 0;
let product: ProductInfo = reactive({
  price: 10,
  quantity: 10,
}); // product数据变成了响应式的
effect(() => (total = product.price * product.quantity)); // 传入需要依赖的函数
log(total, 1); // 100
product.quantity = 100;
log(total, 2); // 1000
log(product.quantity);
product.price = 100;
log(total, 3); // 10000
log(product.price);

let salePrice: number = 0; // 假设我们的售卖价格是货物价格的七折
effect(() => (salePrice = product.price * 0.7)); // 传入需要依赖的函数
log(salePrice, 4);
log(total, 5);
product.price = 50;
log(salePrice, 6);
log(total, 7);
// 数据依然是响应式的，且这次我们做到了只有在函数运行中依赖收集系统才会进行依赖的收集
